import GameConfig from './GameConfig'
import { Square } from './Square'
import { SquareGroup } from './SquareGroup'
import { IPoint, MoveDirection, Shape } from './types'

function isPoint(obj: any): obj is IPoint {
  if (typeof obj.x === 'undefined') {
    return false
  }
  return true
}

export class TerisRule {
  static canIMove(shape: Shape, targetPoint: IPoint, exists: Square[]): boolean {
    const targetPoints: IPoint[] = shape.map(item => ({
      x: item.x + targetPoint.x,
      y: item.y + targetPoint.y
    }))

    const isBoundary = targetPoints.some(item => {
      if (
        item.x < 0 ||
        item.x > GameConfig.panelSize.width - 1 ||
        item.y < 0 ||
        item.y > GameConfig.panelSize.height - 1
      ) {
        return true
      }
    })

    if (isBoundary) {
      return false
    }

    // 检查是否与已有方块重叠
    const res = targetPoints.some(p =>
      exists.some(item => item.point.x === p.x && item.point.y === p.y)
    )
    if (res) {
      return false
    }

    return true
  }

  static move(tertis: SquareGroup, targetPoint: IPoint, exists: Square[]): boolean
  static move(tertis: SquareGroup, targetPointDirection: MoveDirection, exists: Square[]): boolean

  static move(
    tertis: SquareGroup,
    targetPointDirection: IPoint | MoveDirection,
    exists: Square[]
  ): boolean {
    if (isPoint(targetPointDirection)) {
      if (this.canIMove(tertis.shape, targetPointDirection, exists)) {
        tertis.centerPoint = targetPointDirection
        return true
      }
      return false
    } else {
      const direction = targetPointDirection
      let targetPoint: IPoint
      if (direction === MoveDirection.down) {
        targetPoint = {
          x: tertis.centerPoint.x,
          y: tertis.centerPoint.y + 1
        }
      } else if (direction === MoveDirection.left) {
        targetPoint = {
          x: tertis.centerPoint.x - 1,
          y: tertis.centerPoint.y
        }
      } else {
        targetPoint = {
          x: tertis.centerPoint.x + 1,
          y: tertis.centerPoint.y
        }
      }
      return this.move(tertis, targetPoint, exists)
    }
  }

  static MoveDirectly(tertis: SquareGroup, direction: MoveDirection, exists: Square[]) {
    while (this.move(tertis, direction, exists)) {}
  }

  static rotate(tertis: SquareGroup, exists: Square[]): boolean {
    const newShape = tertis.afterRotateShape()
    if (this.canIMove(newShape, tertis.centerPoint, exists)) {
      tertis.rotate()
      return true
    } else {
      return false
    }
  }
  static getLineSquares(exists: Square[], y: number) {
    return exists.filter(sq => sq.point.y === y)
  }
  static deleteSquares(exists: Square[]): number {
    const ys = exists.map(sq => sq.point.y)
    const maxY = Math.max(...ys)
    const minY = Math.min(...ys)
    let num = 0
    for (let i = minY; i <= maxY; i++) {
      if (this.deleteLine(exists, i)) {
        num++
      }
    }
    return num
  }
  static deleteLine(exists: Square[], y: number): boolean {
    const squares = this.getLineSquares(exists, y)
    if (squares.length === GameConfig.panelSize.width) {
      squares.forEach(sq => {
        if (sq.viewer) {
          sq.viewer.remove()
        }
        const index = exists.indexOf(sq)
        exists.splice(index, 1)
      })
      exists
        .filter(item => item.point.y < y)
        .forEach(sq => {
          sq.point = {
            x: sq.point.x,
            y: sq.point.y + 1
          }
        })
      return true
    }
    return false
  }
}
